﻿@*
//------------------------------------------------------------------------------
//  此代码版权声明为全文件覆盖，如有原作者特别声明，会在下方手动补充
//  此代码版权（除特别声明外的代码）归作者本人Diego所有
//  源代码使用协议遵循本仓库的开源协议及附加协议
//  Gitee源代码仓库：https://gitee.com/diego2098/ThingsGateway
//  Github源代码仓库：https://github.com/kimdiego2098/ThingsGateway
//  使用文档：https://diego2098.gitee.io/thingsgateway-docs/
//  QQ群：605534569
//------------------------------------------------------------------------------
*@

@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@namespace ThingsGateway.OPCUA
@using Opc.Ua;
@using System.Reflection;
@using ThingsGateway.Foundation.Adapter.OPCUA;
@using ThingsGateway.Foundation;
@using ThingsGateway.Web.Foundation;
@using Masa.Blazor
@using Yitter.IdGenerator;
<MCard Class="ma-0">
    <MCardTitle Class="indigo white--text text-h6">
        @PLC?.OPCNode?.OPCURL
    </MCardTitle>
    <MRow Class="pa-4"
          Justify="JustifyTypes.SpaceBetween">
        <MCol Cols="12" Md="6">
            <MTreeview @bind-Value=_selected @bind-Active=_actived SelectionType="SelectionType.Independent"
                       Items=Nodes TItem=OPCUATagModel
                       Color="warning" Selectable Activatable
                       LoadChildren=PopulateBranchAsync
                       TKey=ReferenceDescription ItemKey="r=>r.Tag"
                       Style="height:500px;overflow-y:auto;"
                       ItemText="r=>r.Name"
                       ItemChildren="r=>r.Nodes">

            </MTreeview>

        </MCol>
        <MCol Cols="12" Md="6">
            <MCardText Style="height:500px;overflow-y:auto;">
                @if (_actived?.Count == 0 || _actived?.FirstOrDefault()?.NodeId==null)
                {
                    <div key="title"
                         class="text-h6 font-weight-light grey--text pa-4 text-center">
                        选择左侧节点
                    </div>
                }
                else
                {
                    <ScrollXTransition>
                        <MSheet Outlined Class="ma-0 pa-1">
                            <MRow Align="AlignTypes.Center">
                                <MCol>
                                    <MListItem>
                                        <ItemContent>
                                            <MListItemContent>
                                                <MListItemTitle>NodeId </MListItemTitle>
                                            </MListItemContent>
                                        </ItemContent>
                                    </MListItem>
                                </MCol>
                                <MDivider Vertical Center />
                                <MCol>
                                    <MLabel Class=@($"ma-1")> @_actived.FirstOrDefault()  </MLabel>
                                </MCol>

                            </MRow>
                        </MSheet>
                        @foreach (var item in nodeAttributes)
                        {
                            <MSheet Outlined Class="ma-0 pa-1">
                                <MRow Align="AlignTypes.Center">
                                    <MCol>
                                        <MListItem>
                                            <ItemContent>
                                                <MListItemContent>
                                                    <MListItemTitle>@item.Name </MListItemTitle>
                                                </MListItemContent>
                                            </ItemContent>
                                        </MListItem>
                                    </MCol>
                                    <MDivider Vertical Center />
                                    <MCol>
                                        <MLabel Class=@($"{(StatusCode.IsBad(item.StatusCode)?"red--text":"green--text")} ma-1")> @item.Value  </MLabel>
                                    </MCol>

                                </MRow>
                            </MSheet>
                        }

                    </ScrollXTransition>

                }
            </MCardText>
        </MCol>

        <MDivider Vertical></MDivider>

    </MRow>

</MCard>
<MOverlay Absolute Value="overlay" Opacity="0.8">
    <span class="green--text text--darken-2">
        @("正在获取OPCUA节点...")
    </span>
    <MProgressCircular Indeterminate Width=6
                       Size="30"></MProgressCircular>
</MOverlay>
@code
{
    [Inject]
    IDriverPluginService DriverPluginService { get; set; }
    public CollectDevice GetImportDevice()
    {
        var data =
               new CollectDevice()
                   {
                       Name = PLC.OPCNode.OPCURL,
                       Id = YitIdHelper.NextId(),
                       Enable = true,
                       IsLogOut = true,
                       DevicePropertys = new(),
                       PluginId = DriverPluginService.GetIdByName("ThingsGateway.OPCUA.OPCUAClient").ToLong(),
                   };

        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.OPCURL), Value = PLC.OPCNode.OPCURL, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.OPCURL)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.UserName), Value = PLC.UserIdentity.DisplayName, Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.UserName)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.Password), Value = "", Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.Password)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.IsUseSecurity), Value = PLC.OPCNode.IsUseSecurity.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.IsUseSecurity)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.ActiveSubscribe), Value = true.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.ActiveSubscribe)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.DeadBand), Value = PLC.OPCNode.DeadBand.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.DeadBand)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.GroupSize), Value = PLC.OPCNode.GroupSize.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.GroupSize)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.UpdateRate), Value = PLC.OPCNode.UpdateRate.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.UpdateRate)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        data.DevicePropertys.Add(new() { PropertyName = nameof(OPCUAClientProperty.ReconnectPeriod), Value = PLC.OPCNode.ReconnectPeriod.ToString(), Description = typeof(OPCUAClientProperty).GetProperty(nameof(OPCUAClientProperty.ReconnectPeriod)).GetCustomAttribute<DevicePropertyAttribute>().Name });
        return data;
    }

    protected override void OnInitialized()
    {
        Task.Run(async () =>
    {
        Nodes = PopulateBranch(ObjectIds.ObjectsFolder);
        overlay = false;
        await InvokeAsync(StateHasChanged);
    });
    }
    private bool overlay = true;
    [Parameter]
    public ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient PLC { get; set; }
    private List<OPCUATagModel> Nodes = new();
    private List<ReferenceDescription> _selected { get; set; } = new();
    private List<ReferenceDescription> actived = new();
    private OPCNodeAttribute[] nodeAttributes;
    private List<ReferenceDescription> _actived
    {
        get => actived;
        set
        {
            if (actived?.FirstOrDefault() != value?.FirstOrDefault() && value?.Count > 0)
            {
                actived = value;
                if (actived.FirstOrDefault().NodeId != null)
                    nodeAttributes = PLC.ReadNoteAttributes(actived.FirstOrDefault().NodeId.ToString());
            }

        }

    }

    private Task PopulateBranchAsync(OPCUATagModel model)
    {
        return Task.Run(() =>
        {
            var sourceId = (NodeId)model.Tag.NodeId;
            model.Nodes = PopulateBranch(sourceId);
        });
    }

    public (CollectDevice, List<CollectDeviceVariable>) GetImportVariableList()
    {
        var device = GetImportDevice();
        var data = _selected.Select(a =>
          {
              var nodeClass = (PLC.ReadNoteAttribute(a.NodeId.ToString(), Attributes.NodeClass).FirstOrDefault().Value).ToString();
              if (nodeClass == nameof(NodeClass.Variable))
              {
                  ProtectTypeEnum level = ProtectTypeEnum.ReadOnly;
                  try
                  {
                      var userAccessLevel = (AccessLevelType)(PLC.ReadNoteAttribute(a.NodeId.ToString(), Attributes.UserAccessLevel).FirstOrDefault().Value);
                      level = (userAccessLevel.HasFlag(AccessLevelType.CurrentRead)) ?
         userAccessLevel.HasFlag(AccessLevelType.CurrentWrite) ?
         ProtectTypeEnum.ReadWrite : ProtectTypeEnum.ReadOnly : ProtectTypeEnum.WriteOnly;
                  }
                  catch
                  {

                  }
                  var dataTypeId = (Opc.Ua.NodeId)(PLC.ReadNoteAttribute(a.NodeId.ToString(), Attributes.DataType).FirstOrDefault().Value);
                  var dataType = Opc.Ua.DataTypes.GetSystemType(dataTypeId, null);
                  var result = Enum.TryParse(typeof(DataTypeEnum), dataType.Name, out object dataTypeEnum);
                  if (!result)
                  {
                      dataTypeEnum = DataTypeEnum.Object;
                  }
                  return new CollectDeviceVariable()
                      {
                          Name = device.Name + "_" + a.DisplayName.Text,
                          VariableAddress = a.NodeId.ToString(),
                          DeviceId = device.Id,
                          DataTypeEnum = (DataTypeEnum)dataTypeEnum,
                          Id = Yitter.IdGenerator.YitIdHelper.NextId(),
                          ProtectTypeEnum = level,
                          IntervalTime = 1000,
                      };
              }
              else
              {
                  return null;
              }
          }).Where(a => a != null).ToList();
        return (device, data);
    }

    private List<OPCUATagModel> PopulateBranch(NodeId sourceId)
    {
        if (!PLC.Connected)
        {
            return new() { new() { Name = "未完成连接", Tag = new(), Nodes = null } };
        }
        List<OPCUATagModel> nodes = new List<OPCUATagModel>();
        nodes.Add(new OPCUATagModel() { Name = "Browsering..." });

        ReferenceDescriptionCollection references = GetReferenceDescriptionCollection(sourceId);
        List<OPCUATagModel> list = new List<OPCUATagModel>();
        if (references != null)
        {
            for (int ii = 0; ii < references.Count; ii++)
            {
                ReferenceDescription target = references[ii];
                OPCUATagModel child = new OPCUATagModel { Name = Utils.Format("{0}", target) };

                child.Tag = target;
                var data = GetReferenceDescriptionCollection((NodeId)target.NodeId);
                if (data != null && data.Count > 0)
                {
                    @*child.Nodes = PopulateBranch((NodeId)target.NodeId);*@
                }
                else
                {
                    child.Nodes = null;
                }
                list.Add(child);
            }
        }

        List<OPCUATagModel> listNode = list;

        nodes.Clear();
        nodes.AddRange(listNode.ToArray());
        return nodes;
    }

    private ReferenceDescriptionCollection GetReferenceDescriptionCollection(NodeId sourceId)
    {
        TaskCompletionSource<ReferenceDescriptionCollection> task = new TaskCompletionSource<ReferenceDescriptionCollection>();

        BrowseDescription nodeToBrowse1 = new BrowseDescription();

        nodeToBrowse1.NodeId = sourceId;
        nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
        nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.Aggregates;
        nodeToBrowse1.IncludeSubtypes = true;
        nodeToBrowse1.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method | NodeClass.ReferenceType | NodeClass.ObjectType | NodeClass.View | NodeClass.VariableType | NodeClass.DataType);
        nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;

        BrowseDescription nodeToBrowse2 = new BrowseDescription();

        nodeToBrowse2.NodeId = sourceId;
        nodeToBrowse2.BrowseDirection = BrowseDirection.Forward;
        nodeToBrowse2.ReferenceTypeId = ReferenceTypeIds.Organizes;
        nodeToBrowse2.IncludeSubtypes = true;
        nodeToBrowse2.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method | NodeClass.View | NodeClass.ReferenceType | NodeClass.ObjectType | NodeClass.VariableType | NodeClass.DataType);
        nodeToBrowse2.ResultMask = (uint)BrowseResultMask.All;

        BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection();
        nodesToBrowse.Add(nodeToBrowse1);
        nodesToBrowse.Add(nodeToBrowse2);

        ReferenceDescriptionCollection references = FormUtils.Browse(PLC.Session, nodesToBrowse, false);
        return references;
    }

    internal class OPCUATagModel
    {
        internal string Name { get; set; }
        internal ReferenceDescription Tag { get; set; }
        internal string NodeId => (Tag.NodeId).ToString();
        internal List<OPCUATagModel> Nodes { get; set; } = new();
    }
}